
<!DOCTYPE html>
<!--[if IEMobile 7 ]><html class="no-js iem7"><![endif]-->
<!--[if lt IE 9]><html class="no-js lte-ie8"><![endif]-->
<!--[if (gt IE 8)|(gt IEMobile 7)|!(IEMobile)|!(IE)]><!--><html class="no-js" lang="en"><!--<![endif]-->
<head>
  <meta charset="utf-8">
  <title>编写高质量代码(下) - Yebangyu's Blog</title>
  <meta name="author" content="Yebangyu">

  
  <meta name="description" content="本文介绍了编写高质量C++代码的关键因素，包括代码规范、Code Review、质量保证等">
  <meta name="keywords" content="C++, 代码规范, Code Review">

  <!-- http://t.co/dKP3o1e -->
  <meta name="HandheldFriendly" content="True">
  <meta name="MobileOptimized" content="320">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  
  <link rel="canonical" href="http://www.yebangyu.org/blog/2016/02/24/writeelegantcodes/">
  <link href="/favicon.png" rel="icon">
  <link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
  <link href="/atom.xml" rel="alternate" title="Yebangyu's Blog" type="application/atom+xml">
  <script src="/javascripts/modernizr-2.0.js"></script>
  <script src="//ajax.lug.ustc.edu.cn/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script>!window.jQuery && document.write(unescape('%3Cscript src="/javascripts/libs/jquery.min.js"%3E%3C/script%3E'))</script>
  <script src="/javascripts/octopress.js" type="text/javascript"></script>
  <!--Fonts from Google"s Web font directory at http://google.com/webfonts -->
<link href="//fonts.lug.ustc.edu.cn/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<link href="//fonts.lug.ustc.edu.cn/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
<!-- mathjax config similar to math.stackexchange -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  jax: ["input/TeX", "output/HTML-CSS"],
  tex2jax: {
    inlineMath: [ ['$', '$'] ],
    displayMath: [ ['$$', '$$']],
    processEscapes: true,
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
  },
  messageStyle: "none",
  "HTML-CSS": { preferredFont: "TeX", availableFonts: ["STIX","TeX"] }
});
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script>

  

</head>

<body   >
  <header role="banner"><hgroup>
  <h1><a href="/">Yebangyu's Blog</a></h1>
  
    <h2>Fond of Concurrency Programming and Machine Learning</h2>
  
</hgroup>

</header>
  <nav role="navigation"><ul class="subscription" data-subscription="rss">
  <li><a href="/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></li>
  
</ul>
  
<form action="https://www.google.com/search" method="get">
  <fieldset role="search">
    <input type="hidden" name="sitesearch" value="www.yebangyu.org">
    <input class="search" type="text" name="q" results="0" placeholder="Search"/>
  </fieldset>
</form>
  
<ul class="main-navigation">
  <li><a href="/">Blog</a></li>
  <li><a href="/blog/archives">Archives</a></li>
  <li><a href="/about">About Me</a></li>
</ul>

</nav>
  <div id="main">
    <div id="content">
      <div>
<article class="hentry" role="article">
  
  <header>
    
      <h1 class="entry-title">编写高质量代码(下)</h1>
    
    
      <p class="meta">
        




<time class='entry-date' datetime='2016-02-24T22:26:44+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>24</span><span class='date-suffix'>th</span>, <span class='date-year'>2016</span></span> <span class='time'>10:26 pm</span></time>
        
      </p>
    
  </header>


<div class="entry-content"><p><a href="http://www.yebangyu.org/blog/2015/11/06/how-to-write-elegant-cpp-codes/">上回</a>我们从微观角度，以一个实际的例子，从正确、高效、易读等特性着手，介绍了如何编写高质量的代码。这次，我们从宏观出发，从软件开发流程入手，着重介绍其中的几个方面，包括代码规范、Code Review、测试等。</p>

<p>如果说<a href="http://www.yebangyu.org/blog/2015/11/06/how-to-write-elegant-cpp-codes/">上回</a>的内容注重个人编码，那么本文将偏向团队开发。</p>

<h2 id="section">代码规范</h2>

<p>团队的代码规范，一般由领导和大佬们制定后，大家统一实行。这里面有几个问题：</p>

<h3 id="section-1">真的需要代码规范吗？</h3>

<p>言下之意，制定和执行代码规范是否浪费时间？</p>

<!--more-->

<p>答案是：It depends。如果项目很庞大、代码质量要求很高，那么，制定和执行代码规范所花费的时间，将大大少于后期因为不规范开发带来的种种调试和维护成本。如果是小打小闹的代码，就无所谓了。</p>

<h3 id="section-2">代码规范的制定为什么这么难？</h3>

<p>原因众多，其中一个很重要的部分是团队每个人的口味和观点不尽相同。就代码风格而言，有人喜欢对内置类型变量i使用i++，有人坚持认为应该使用++i不管i是不是复杂类型。因此，制定代码规范需要在讨论之后最后拍板决定，这里面甚至需要独裁！是的，独裁！</p>

<h3 id="section-3">代码规范制定需要注意什么事项？</h3>

<p>如果代码规范限制太松，那么等于没有规范；如果太严，大大影响开发效率。这里面的尺度，需要根据项目需要、团队成员特点全面考量，进行取舍。</p>

<p>需要注意的是，没有任何一种代码规范是完美的。例如，在C++中，如果启用异常，那么代码的流程将会被各种异常处理中断，各种try catch throw让代码很不美观；如果禁用异常，也就是在开发的过程中不能使用异常特性，那么团队成员可能因为长期没有接触这项语言feature而造成知识和技能短板。</p>

<h3 id="section-4">代码风格举例</h3>

<p>举两个我认为比较重要、比较新鲜、比较有趣的代码风格。</p>

<p>1，使用引用需要判空吗？</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
</pre></td><td class="code"><pre><code class="c++"><span class="line"><span class="kt">void</span> <span class="nf">f</span><span class="p">(</span><span class="kt">int</span> <span class="o">&amp;</span><span class="n">p</span><span class="p">);</span>
</span><span class="line"><span class="kt">void</span> <span class="nf">g</span><span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="n">p</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>

<p>我们都知道，在g中，使用*p前需要对p是否为NULL进行判断，那么f呢？如果质量非常关键、代码安全非常重要的场景，那么实际上，也是需要的。因为调用者可能这样：</p>
<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
</pre></td><td class="code"><pre><code class="c++"><span class="line"><span class="kt">int</span> <span class="o">*</span><span class="n">q</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
</span><span class="line"><span class="c1">//......</span>
</span><span class="line"><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">q</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>因此，需要在f里增加<code>if(NULL == &amp;p)</code>的判断。</p>

<p>2，级联if else语句。</p>

<p>首先看一个我个人认为不好的代码风格：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
<span class="line-number">14</span>
<span class="line-number">15</span>
<span class="line-number">16</span>
</pre></td><td class="code"><pre><code class="c++"><span class="line"><span class="kt">int</span> <span class="nf">f</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span>
</span><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">    <span class="k">if</span> <span class="p">(</span><span class="n">b</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">      <span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&gt;=</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">        <span class="c1">//do sth</span>
</span><span class="line">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class="line">        <span class="c1">//error1</span>
</span><span class="line">      <span class="p">}</span>
</span><span class="line">    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class="line">      <span class="c1">//error2</span>
</span><span class="line">    <span class="p">}</span>
</span><span class="line">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class="line">    <span class="c1">//error3</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>这个函数的核心在于do sth部分。其实我们可以改写为级联if－else形式，如下：</p>

<div class="bogus-wrapper"><notextile><figure class="code"><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class="line-number">1</span>
<span class="line-number">2</span>
<span class="line-number">3</span>
<span class="line-number">4</span>
<span class="line-number">5</span>
<span class="line-number">6</span>
<span class="line-number">7</span>
<span class="line-number">8</span>
<span class="line-number">9</span>
<span class="line-number">10</span>
<span class="line-number">11</span>
<span class="line-number">12</span>
<span class="line-number">13</span>
</pre></td><td class="code"><pre><code class="c++"><span class="line"><span class="kt">int</span> <span class="nf">f</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">,</span> <span class="kt">int</span> <span class="n">b</span><span class="p">)</span>
</span><span class="line"><span class="p">{</span>
</span><span class="line">  <span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">    <span class="c1">//error3</span>
</span><span class="line">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">b</span> <span class="o">&lt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">    <span class="c1">//error2</span>
</span><span class="line">  <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">a</span> <span class="o">&lt;</span> <span class="n">b</span><span class="p">)</span> <span class="p">{</span>
</span><span class="line">    <span class="c1">//error1</span>
</span><span class="line">  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class="line">    <span class="c1">//so, a&gt;=1 &amp;&amp; b&gt;=1 &amp;&amp; a&gt;=b</span>
</span><span class="line">    <span class="c1">//do sth</span>
</span><span class="line">  <span class="p">}</span>
</span><span class="line"><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure></notextile></div>
<p>是不是优美多了？前面只做一些错误处理、前期准备、参数检查等，最后的else分支做实实在在的功能性事情。</p>

<h2 id="code-review">Code Review</h2>

<h3 id="code-review-1">什么是Code Review？</h3>

<p>很多人把它翻译为代码审查，我觉得太政治味了。程序员尤其是新手写完代码后，可能会有风格问题（比如不符合团队的代码规范）、安全性问题(比如忘记指针判空)、优雅性问题(比如大量冗余代码)、正确性问题（比如算法设计错误），那么在发布代码到公共库之前，提交给师兄或者mentor，让他帮你review一下代码，并提出可能的问题和建议，让你好好修改。这样的过程，就叫做Code Review。</p>

<h3 id="section-5">我的天呐，那这不是很占用时间？</h3>

<p>是的。一个写代码，一个看代码，看代码的时间可能并不比全新写一份代码少。那么，这又是何必呢？</p>

<p>主要的原因有：</p>

<p>1，review确实占用了开发时间，然而开发，或者说写代码，其实只占很少的时间比例。很多时间花在debug、调试、写文档、需求分析、设计算法、维护等等上。</p>

<p>2，代码质量非常重要，这点时间投入是值得的。与其后期苦逼追bug，不如前期多投入点时间和人力。</p>

<p>3，培养新人，让新手更快成长。</p>

<h3 id="code-review-2">如何更好的执行Code Review</h3>

<p>这里给几点建议：</p>

<p>1，不走过场。走过场，还不如不要这个流程。</p>

<p>2，作为Reviewer，看不懂代码就把作者拉过来，当面询问，不要不懂装懂，也不要爱面子不好意思问。</p>

<p>3，作为Coder，心里要有感激之情。真的。不要得了便宜还卖乖，感恩reviewer，感激reviewer对自己的进步和成长所做出的贡献，所花费的心血。中国人里狼心狗肺、忘恩负义、不懂感恩的人还算少吗？</p>

<p>4，作为Coder，给Reviewer Review之前，请先做单元测试并确保通过，并自己尝试先整体看一遍自己本次提交的代码。注意，不要给别人提还没调试通过的代码，这是非常不尊重别人的表现。</p>

<h2 id="section-6">质量保证</h2>

<p>1，测试不是专属QA的活儿，对自己写的代码提供质量保证，是程序员的职责。QA要负责的，是系统的质量，不是模块的质量。</p>

<p>2，测试，需要意识，需要坚持。我发现C++程序员、前端程序员的测试意识或者说质量意识最强；数据科学家或者数据工程师的质量意识最差，很多人甚至不写测试用例。当然，这不怪他们，毕竟，有时候代码里有个bug，准确率和召回率会更高。</p>

<p>3，测试用例的编写和设计需要保证一定的代码覆盖率，力求让每个分支和流程的代码都走到，然后分析运行结果是否是符合期望的，不要只考虑正确路径上的那些分支。</p>

<p>4，测试用例的编写和设计力求全面，考虑到方方面面。以非常经典的二分搜索为例：</p>

<p>int binary_search(int *p, int n, int target, int &amp;idx);</p>

<p>binary_search函数返回值为0表示成功执行，输出参数idx返回target在有序数组p中(第一次出现)的位置，－1表示不存在。</p>

<p>那么测试用例至少应该涵盖：</p>

<ul>
  <li>
    <p>p为NULL的情况</p>
  </li>
  <li>
    <p>数组大小n分别为负数、0、1、2时情况</p>
  </li>
  <li>
    <p>数组p不是有序数组的情况</p>
  </li>
  <li>
    <p>target在数组中出现0次、1次、n次的情况</p>
  </li>
</ul>

<p>你是否都考虑到了呢？</p>

<p>4，有时候，自己书写测试用例显得刀耕火种，现在已经有很多辅助的工具，读者可以自行google一下。</p>
</div>


  <footer>
    <p class="meta">
      
  

<span class="byline author vcard">Posted by <span class="fn">Yebangyu</span></span>

      




<time class='entry-date' datetime='2016-02-24T22:26:44+08:00'><span class='date'><span class='date-month'>Feb</span> <span class='date-day'>24</span><span class='date-suffix'>th</span>, <span class='date-year'>2016</span></span> <span class='time'>10:26 pm</span></time>
      

<span class="categories">
  
    <a class='category' href='/blog/categories/c-plus-plus/'>c++</a>
  
</span>


    </p>
    
      <div class="sharing">
  
  
  
</div>

    
    <p class="meta">
      
        <a class="basic-alignment left" href="/blog/2016/02/20/object-function-in-cpp/" title="Previous Post: C++函数对象(function object)的应用">&laquo; C++函数对象(function object)的应用</a>
      
      
        <a class="basic-alignment right" href="/blog/2016/03/04/petersonalgorithm/" title="Next Post: Peterson算法实现spin lock">Peterson算法实现spin lock &raquo;</a>
      
    </p>
  </footer>
</article>


</div>

<aside class="sidebar">
  
    <section>
  <h1>Recent Posts</h1>
  <ul id="recent_posts">
    
      <li class="post">
        <a href="/blog/2017/03/11/2017/">2017技术成长之路</a>
      </li>
    
      <li class="post">
        <a href="/blog/2017/02/17/virtualfunctionandvariadicparametertemplate/">虚函数和变长参数模板的妙用</a>
      </li>
    
      <li class="post">
        <a href="/blog/2016/12/25/singleton/">Singleton与多线程</a>
      </li>
    
      <li class="post">
        <a href="/blog/2016/12/04/introductiontohazardpointer/">Lock Free中的Hazard Pointer(下)</a>
      </li>
    
      <li class="post">
        <a href="/blog/2016/12/03/gccandperfopt/">性能优化的那些传说和迷思</a>
      </li>
    
  </ul>
</section>
<section>
  <h1>Friends' Link</h1>
  <ul>
    <li>
	  <li><a href="http://www.chongh.wiki/">Diting0x</a></li>
	  <li><a href="http://www.xiaolili.net/">wangli</a></li>
	  <li><a href="http://www.skykewei.top">dukewei</a></li>
	  <li><a href="http://irwenqiang.github.io">chenwenqiang</a></li>
      <li><a href="http://www.armsword.com">duruofei</a></li>
    </li>
  </ul>
</section><section>
  <h1>Yebangyu</h1>
  <p>福建人。热爱历史、K歌、NBA</p>
  <p>帝都码农</p>
  <p>历史学家，专治秦汉史</p>
</section>
<section>
 <h1>Categories</h1>
 <ul id="categories">
  <li class='category'><a href='/blog/categories/c-plus-plus/'>c++ (11)</a></li>
<li class='category'><a href='/blog/categories/soupen/'>soupen (4)</a></li>
<li class='category'><a href='/blog/categories/web/'>web (1)</a></li>
<li class='category'><a href='/blog/categories/qi-ta/'>其他 (5)</a></li>
<li class='category'><a href='/blog/categories/li-shi/'>历史 (2)</a></li>
<li class='category'><a href='/blog/categories/bing-xing-bian-cheng/'>并行编程 (20)</a></li>
<li class='category'><a href='/blog/categories/xing-neng-you-hua/'>性能优化 (2)</a></li>
<li class='category'><a href='/blog/categories/suan-fa/'>算法 (4)</a></li>
<li class='category'><a href='/blog/categories/bian-yi-lian-jie/'>编译链接 (2)</a></li>

 </ul>
</section>




  
</aside>


    </div>
  </div>
  <footer role="contentinfo"><!-- mathjax config similar to math.stackexchange -->
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  jax: ["input/TeX", "output/HTML-CSS"],
  tex2jax: {
    inlineMath: [ ['$', '$'] ],
    displayMath: [ ['$$', '$$']],
    processEscapes: true,
    skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
  },
  messageStyle: "none",
  "HTML-CSS": { preferredFont: "TeX", availableFonts: ["STIX","TeX"] }
});
</script>
<script src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" type="text/javascript"></script>
<p>
  Copyright &copy; 2017 - Yebangyu -
  <span class="credit">Powered by <a href="http://octopress.org">Octopress</a></span>
</p>
<script type="text/javascript">var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");document.write(unescape("%3Cspan id='cnzz_stat_icon_1257548193'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s11.cnzz.com/z_stat.php%3Fid%3D1257548193' type='text/javascript'%3E%3C/script%3E"));</script>

</footer>
  











</body>
</html>
